package lumis.portal.page.serialization;

import lumis.portal.PortalObjectNotFoundException;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.page.PageConfig;
import lumis.portal.serialization.DeserializationContext;
import lumis.util.ITransaction;

import java.util.Collection;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Utility class for serialization operations related to page layouts
 *
 * @version $Revision: 12321 $ $Date: 2011-01-05 10:28:18 -0200 (Wed, 05 Jan 2011) $
 * @since 4.0.11
 */
public abstract class PageLayoutUtil
{
	private static final Pattern cellPattern = Pattern.compile("(<cell[^>]*>)(.*?)(</cell>)");
	private static final Pattern interfacePattern = Pattern.compile("(<interface[^>]*id=\")([^\"]+)");

	/**
	 * Apply deserialization id mappings to a page layout.
	 * 
	 * @param deserializationContext
	 * @param pageConfig contains the layout to be mapped 
	 */
	public static void applyIdMappings(DeserializationContext deserializationContext, PageConfig pageConfig)
	{
		if (pageConfig.getLayout() != null)
		{
			boolean usesCellPattern = pageConfig.getLayout().contains("cell");
			Matcher m = (usesCellPattern ? cellPattern : interfacePattern).matcher(pageConfig.getLayout());
			StringBuffer sb = new StringBuffer();
			while (m.find())
			{
				String serviceInterfaceInstanceId = m.group(2);
				String newServiceInterfaceInstanceId = serviceInterfaceInstanceId;
				if (serviceInterfaceInstanceId.length() > 0)
					newServiceInterfaceInstanceId = deserializationContext.getIdMapping(serviceInterfaceInstanceId);
				m.appendReplacement(sb, "$1" + newServiceInterfaceInstanceId + (usesCellPattern ? "$3" : ""));
			}
			m.appendTail(sb);
			
			pageConfig.setLayout(sb.toString());
		}
	}
	
	/**
	 * Returns a collection of service interface instances' identifiers, referenced by the page's layout, 
	 * that represents violated foreign keys.
	 * 
	 * @param deserializationContext
	 * @param pageConfig contains the layout that reference the service interface instances to be checked
	 * @return collection of referenced service interface instances that do not exist 
	 */
	public static Collection<String> getViolatedForeignKeys(DeserializationContext deserializationContext, PageConfig pageConfig)
	{
		HashSet<String> violatedKeys = new HashSet<String>();
		
		String layout = pageConfig.getLayout();
		
		if (layout != null)
		{
			// analyze foreign keys present in page layout
			Matcher m = (layout.contains("cell") ? cellPattern : interfacePattern).matcher(layout);
			while (m.find())
			{
				String serviceInterfaceInstanceId = m.group(2);
				if (serviceInterfaceInstanceId.length() > 0)
				{
					boolean foreignKeyViolated = false;
					String mappedServiceInterfaceInstanceId = deserializationContext.getIdMapping(serviceInterfaceInstanceId);
					if (deserializationContext.isCreateNewIds())
						foreignKeyViolated = !deserializationContext.hasIdMapping(serviceInterfaceInstanceId);
					else
						foreignKeyViolated = !serviceInterfaceInstanceExists(deserializationContext.getSessionConfig(), mappedServiceInterfaceInstanceId, deserializationContext.getTransaction());
					if (foreignKeyViolated)
					{
						deserializationContext.logDebug(" layout foreign key violated: serviceInterfaceInstanceId = '" + serviceInterfaceInstanceId + "'");						
						violatedKeys.add(serviceInterfaceInstanceId);
					}
				}
			}
		}

		return violatedKeys;
	}

	/**
	 * Returns true if the given service interface instance exists.
	 * 
	 * @param sessionConfig
	 * @param serviceInterfaceInstanceId service interface instance to be checked.
	 * @param transaction
	 * @return true if the given service interface instance exists.
	 */
	private static boolean serviceInterfaceInstanceExists(SessionConfig sessionConfig, String serviceInterfaceInstanceId, ITransaction transaction)
	{
		boolean exists = false;
		try
		{
			ManagerFactory.getServiceInterfaceInstanceManager().get(sessionConfig, serviceInterfaceInstanceId, transaction);
			exists = true;
		}
		catch (PortalObjectNotFoundException e)
		{
			exists = false;
		}
		catch (Exception e)
		{
			throw new RuntimeException("Error getting service interface instance", e);
		}
		return exists;
	}

}
